class JsonRpcProtocol {
  static const String version = '2.0';
}

class JsonRpcRequest {
  final String jsonrpc = JsonRpcProtocol.version;
  final String method;
  final Map<String, dynamic>? params;
  final String? id;

  JsonRpcRequest({required this.method, this.params, this.id});

  Map<String, dynamic> toJson() => {
        'jsonrpc': jsonrpc,
        'method': method,
        if (params != null) 'params': params,
        if (id != null) 'id': id,
      };
}

class JsonRpcResponse {
  final String jsonrpc;
  final dynamic result;
  final JsonRpcError? error;
  final String? id;

  JsonRpcResponse({
    this.jsonrpc = JsonRpcProtocol.version,
    this.result,
    this.error,
    this.id,
  });

  factory JsonRpcResponse.fromJson(Map<String, dynamic> json) {
    return JsonRpcResponse(
      jsonrpc: json['jsonrpc'] as String,
      result: json['result'],
      error:
          json['error'] != null ? JsonRpcError.fromJson(json['error']) : null,
      id: json['id']?.toString(),
    );
  }

  Map<String, dynamic> toJson() => {
        'jsonrpc': jsonrpc,
        if (result != null) 'result': result,
        if (error != null) 'error': error!.toJson(),
        if (id != null) 'id': id,
      };
}

class JsonRpcError implements Exception {
  final int code;
  final String message;
  final dynamic data;

  JsonRpcError(this.code, this.message, [this.data]);

  factory JsonRpcError.fromJson(Map<String, dynamic> json) {
    final code = json['code'] as int;
    final enumCode = JsonRpcErrorCode.fromCode(code);
    return JsonRpcError(
      code,
      enumCode?.message ?? json['message'],
      json['data'],
    );
  }

  factory JsonRpcError.fromCode(JsonRpcErrorCode code, [dynamic data]) {
    return JsonRpcError(code.code, code.message, data);
  }

  Map<String, dynamic> toJson() => {
        'code': code,
        'message': message,
        if (data != null) 'data': data,
      };

  @override
  String toString() => 'JsonRpcError($code, $message, $data)';
}

/// JSON-RPC 2.0 标准错误码
enum JsonRpcErrorCode {
  parseError(-32700, 'Parse error'),
  invalidRequest(-32600, 'Invalid Request'),
  methodNotFound(-32601, 'Method not found'),
  invalidParams(-32602, 'Invalid params'),
  internalError(-32603, 'Internal error'),
  serverError(-32000, 'Server error'); // -32000 ~ -32099

  final int code;
  final String message;

  const JsonRpcErrorCode(this.code, this.message);

  static JsonRpcErrorCode? fromCode(int code) {
    for (final e in JsonRpcErrorCode.values) {
      if (e.code == code) return e;
    }
    // -32000 ~ -32099 范围内的都算 serverError
    if (code <= -32000 && code >= -32099) {
      return JsonRpcErrorCode.serverError;
    }
    return null;
  }
}
